﻿/**
 * @file guild_module_loader.hpp
 * @date 2021/05/11 21:13
 *
 * @author harrybao (harrybao@123u.com)
 *
 * @brief 公会模块加载
 *
 * @version v1.0.0
 *
 * @copyright Copyright © 2021 All Rights Reserved 上海欢乐互娱网络科技有限公司
 *
 * @par history:
 * <table>
 * <tr><th>Date                     <th>Version     <th>Author <th>Description
 * <tr><td>2021/05/11 21:13         <td>1.0.0       <td>harrybao
 * <td>创建初始版本 <tr><td>2021/05/19 11:05         <td>1.1.0 <td>harrybao
 * <td>使用ORM
 * </table>
 */

// #ifndef GUILD_MODULE_LOADER_HPP
// #define GUILD_MODULE_LOADER_HPP

#pragma once

#include <cassert>
#include <sstream>

#include "base64/base64.h"
#include "guild/guild_dump.hpp"
#include "guild/module/guild_module.h"
#include "log/service_logger.hpp"
#include "orm/guild_module_orm.hpp"
#include "pool/service_data_pool_op.hpp"
#include "redis/service_redis_fwd.hpp"
#include "guild/guild_redis_key_def.h"

namespace guild_service {

namespace detail {

// extern
// template <typename Module>
// bool SerializeToString(const Module &module, std::string &buffer);

// extern
// template <typename Module>
// bool DeserializeFromString(const std::string &buffer, Module &module);

namespace internal {

static std::string sPbStr;
/**
 * @brief 同步加载公会模块
 *
 * @tparam Msg 需要序列化的数据结构类模板
 * @param service_name 公会服务名
 * @param msg 需要序列化的数据结构类
 * @param guild_id 公会id
 * @param module_name 模块名
 * @return true 加载公会模块成功
 * @return false 加载公会模块失败
 */
template <typename Msg>
static bool LoadGuildModule(const std::string &service_name, Msg &msg,
                            const guild_id_type guild_id,
                            const std::string &module_name) {
  std::stringstream ss;
  ss << "HGET"
     << " guild:" << guild_id << " " << module_name;

  auto ret = redis_command_co(service_name, ss.str(), 1000);
  if (!ret) {
    LOG_FAIL("redis处理失败 命令:%s", ss.str().c_str());
    return false;
  }
  if (!ret->is_success()) {
    LOG_FAIL("redis处理失败 命令:%s 错误:%s 错误码:%u", ss.str().c_str(),
             ret->get_error().c_str(),
             static_cast<uint32_t>(ret->get_error_code()));
    return false;
  }

  if (redis_reply_is_nil(*ret)) {
    LOG_DEBUG("redis获取返回值为nil 命令:%s", ss.str().c_str());
    return true;
  }

  // std::string pb_str;
  if (!ret->get_return(sPbStr)) {
    LOG_FAIL("redis获取返回值失败 命令:%s", ss.str().c_str());
    return false;
  }
  const std::string pb_str_decode = base64_decode(sPbStr);

  if (!DeserializeFromString(pb_str_decode, msg)) {
    LOG_FAIL("公会模块序列化失败 命令:%s 字符串:(%s)", ss.str().c_str(),
             pb_str_decode.c_str());
    return false;
  }

  return true;
}

/**
 * @brief 异步加载公会模块
 *
 * @tparam Serializer
 * @param service_name
 * @param guild_id
 * @param name
 * @param handler
 * @return true
 * @return false
 */
static bool LoadGuildModuleAsync(const std::string &service_name,
                                 const guild_id_type guild_id,
                                 const std::string &module_name,
                                 const redis_handler_type &handler) {
  std::stringstream ss;
  ss << kRedisHGetOp << " " << kGuildKeyPrefix << guild_id << " "
     << module_name;

  if (!redis_command_async(service_name, ss.str(), kRedisCommonTimeOutAsync,
                           handler, guild_id)) {
    LOG_FAIL("redis处理失败 命令:%s", ss.str().c_str());
    return false;
  }

  return true;
}

}  // namespace internal

/**
 * @brief 获取新模块数据池索引下标
 *
 * @return pool_index_type
 */
inline static pool_index_type NewPoolIndex() {
  /// 最后获取数据池索引下标
  static pool_index_type pool_index = 0;
  return pool_index++;
}

// TODO bench Load && Save

/**
 * @brief 同步获取公会模块
 *
 * @tparam Module 公会模块模板
 * @param service_name 公会服务名
 * @param guild_id 公会id
 * @param module_name 公会模块名
 * @param data 公会模块
 * @return true 获取成功
 * @return false 获取失败
 */
template <typename Module>
static bool GetGuildModule(const std::string &service_name,
                           const guild_id_type &guild_id,
                           const std::string &module_name, Module &data) {
  if (!internal::LoadGuildModule(service_name, data, guild_id, module_name)) {
    LOG_FAIL("redis获取公会模块失败 公会id:%llu 模块名:%s 服务名:%s", guild_id,
             module_name.c_str(), service_name.c_str());
    return false;
  }

  LOG_DEBUG("redis同步加载公会模块成功 公会id:%llu 模块名:%s 服务名:%s",
            guild_id, module_name.c_str(), service_name.c_str());

  return true;
}

/**
 * @brief 同步获取公会模块
 *
 * @tparam Serializer 序列化处理类模板
 * @param service_name 公会服务名
 * @param guild 公会
 * @param use_cache 使用缓存
 * @return Serializer::DataType* 公会模块
 */
template <typename Module>
static Module *GetGuildModule(const std::string &service_name,
                              const Guild &guild,
                              const std::string &module_name,
                              GetModulePriority priority) {
  using DataPoolType = common::singleton<service_data_pool<Module>>;

  // TODO check valid

  if (priority == GetModulePriority::GET_MODULE_PRI_USE_CACHE) {
    auto &data = DataPoolType::instance()->get(guild.pool_index);
    if (!DataPoolType::instance()->is_empty_data(data)) {
      return &data;
    }
  }

  Module tmp_data;
  if (!GetGuildModule<Module>(service_name, guild.id, module_name, tmp_data)) {
    return nullptr;
  }

  auto &data = DataPoolType::instance()->get(guild.pool_index);
  data = tmp_data;

  return &data;
}

static std::string sPbStr;

/**
 * @brief 异步获取公会模块
 *
 * @tparam Serializer 序列化处理类模板
 * @param service_name 公会服务名
 * @param guild_id 公会id
 * @return Serializer::DataType& 公会模块
 */
template <typename Module>
static bool GetGuildModuleAsync(const std::string &service_name,
                                const guild_id_type &guild_id,
                                const std::string &module_name,
                                const redis_handler_type &callback) {
  // using DataPoolType = common::singleton<service_data_pool<Module>>;

  if (!internal::LoadGuildModuleAsync(service_name, guild_id, module_name,
                                      callback)) {
    LOG_FAIL("redis获取公会模块失败 公会id:%lld 模块名:%s 服务名:%s",
             guild_id, module_name.c_str(),
             service_name.c_str());
    return false;
  }

  return true;
}

}  // namespace detail
}  // namespace guild_service

// #endif  // GUILD_MODULE_LOADER_HPP